/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.tukano.jpgi.test;

import it.tukano.lang.StringExt;
import it.tukano.log.Log;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Locale;
import java.util.Scanner;

/**
 *
 * @author utente4
 */
public class DecimalSpeller {
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Digitare una cifra e premere invio");
        while(true) {
            String line = in.nextLine();
            if(line.trim().isEmpty()) return;
            String text = new DecimalSpeller().spell(line);
            System.out.println(text);
        }
    }
    
    private static final DecimalSpeller INSTANCE = new DecimalSpeller();
    
    public static DecimalSpeller getInstance() {
        return INSTANCE;
    }
    
    public static DecimalSpeller newInstance() {
        return newInstance(Locale.getDefault());
    }
    
    public static DecimalSpeller newInstance(Locale locale) {
        if(locale.getLanguage().equals(Locale.ITALIAN.getLanguage())) {
            return new DecimalSpeller();
        } else {
            Log.message("Only IT available");
            return new DecimalSpeller();
        }
    }
    
    private DecimalSpeller() {}
    
    public String spell(BigDecimal value) {
        if(value == null) return "/00";
        
        return spell(value.toString());
    }
    
    public String spell(String numberChars) {
        StringExt num = StringExt.wrap(numberChars);
        BigDecimal number = num.toBigDecimal(null);
        if(number == null) {
            Log.message("Errore nella conversione da stringa a BigDecimal");
            return "/00";
        }
        int[] integerDigits = numberToIntegerDigits(number);
        int[] decimalDigits = numberToDecimalDigits(number);
        String integerString = integerDigitsToWord(integerDigits);
        String decimalString = decimalDigitsToWord(decimalDigits);
        return integerString + "/" + decimalString;
    }

    private int[] numberToIntegerDigits(BigDecimal number) {
        int[] digits = new int[15];
        String string = number.toString().split("\\.")[0];
        int off = digits.length - string.length();
        for (int i = 0; i < string.length(); i++) {
            int index = i + off;
            digits[index] = Integer.parseInt(string.substring(i, i + 1));
        }
//        for (int i = 0; i < digits.length; i++) {
//            System.out.print("[" + digits[i] + "]");
//        }
//        System.out.println("");
        return digits;
    }

    private int[] numberToDecimalDigits(BigDecimal number) {
        int[] digits = new int[2];
        Arrays.fill(digits, 0);
        number = number.movePointRight(2);
        BigInteger integer = number.toBigInteger();
        String integerString = integer.toString();
        if(integerString.length() == 0) integerString = "00";
        else if(integerString.length() < 2) integerString = "0" + integerString;
        digits[0] = Integer.parseInt(integerString.substring(integerString.length() - 2, integerString.length() - 1));
        digits[1] = Integer.parseInt(integerString.substring(integerString.length() - 1, integerString.length()));
        return digits;
    }

    private String integerDigitsToWord(int[] integerDigits) {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < integerDigits.length; i++) {
            int pos = i;
            int last = i == integerDigits.length - 1 ? -1 : integerDigits[i + 1];
            int digit = integerDigits[i];
            String text = digitToWord(integerDigits, digit, i);
            boolean endsWithVowel = endsWithElidableVowel(buffer);
            boolean startsWithVowel = startsWithElidableVowel(text);
            if((pos > integerDigits.length - 5) && elidable(buffer, text)) {
                buffer.deleteCharAt(buffer.length() - 1);
            }
            buffer.append(text);
        }
        return buffer.toString();
    }
    
    private boolean elidable(CharSequence a, CharSequence b) {
        if(a.length() == 0) return false;
        if(b.length() == 0) return false;
        if(endsWith(a, "mille")) return false;
        if(endsWith(a, "cento")) return false;
        if(endsWith(a, "mila")) return false;
        return endsWithElidableVowel(a) && startsWithElidableVowel(b);
    }
    
    private boolean endsWith(CharSequence s, String token) {
        if(s.length() < token.length()) return false;
        return s.subSequence(s.length() - token.length(), s.length()).equals(token);
    }
    
    private boolean startsWith(CharSequence s, char c) {
        return s.charAt(0) == c;
    }
    
    private boolean endsWith(CharSequence s, char c) {
        return s.charAt(s.length() - 1) == c;
    }
    
    private boolean endsWithElidableVowel(CharSequence c) {
        if(c.length() == 0) {
            return false;
        }
        char lastChar = c.charAt(c.length() - 1);
        return "aeiou".indexOf(lastChar) >= 0;
    }
    
    private boolean startsWithElidableVowel(CharSequence c) {
        if(c.length() == 0) return false;
        int i = "aeiou".indexOf(c.charAt(0));
        return i >= 0;
    }
    
    private String combine(CharSequence pre, CharSequence post) {
        if(endsWith(pre, "mille")) return pre.toString() + post.toString();
        if(endsWith(pre, "cento")) return pre.toString() + post.toString();
        if(endsWith(pre, "mila")) return pre.toString() + post.toString();
        if(endsWith(pre, "milione")) return pre.toString() + post.toString();
        if(endsWith(pre, "milioni")) return pre.toString() + post.toString();
        if(elidable(pre, post)) {
            return pre.subSequence(0, pre.length() - 1).toString() + post;
        } else {
            return pre.toString() + post;
        }
    }

    private String decimalDigitsToWord(int[] decimalDigits) {
        StringBuilder buffer = new StringBuilder(2);
        buffer.append(decimalDigits[0]);
        buffer.append(decimalDigits[1]);
        return buffer.toString();
    }

    private String digitToWord(int[] digitsArray, int d, int i) {
        int next = i == digitsArray.length - 1 ? -1 : digitsArray[i + 1];
        int nextNext = i > digitsArray.length - 3 ? 0 : digitsArray[i + 2];
        
        int prev = i == 0 ? 0: digitsArray[i - 1];
        int prevPrev = i < 2 ? 0 : digitsArray[i - 2];
        
        int pos = digitsArray.length - i;
        
        switch(pos) {
            case 1: 
                if(d == 0) return "";
                return prev != 1 ? UNITS[d] : "";
            case 2:
                if(d == 0) return "";
                if(d == 1) return TEN_TO_NINETEEN[next == 0 ? 0 : next];
                else return TENTHS[d-1];
            case 3:
                if(d == 0) return "";
                return UNDREDS[d - 1];
            case 4:
                if(d  != 0 && prev == 0 && prevPrev == 0) {
                    if(d == 1) return "mille";
                    return UNITS[d] + "mila";
                }
                
                if(d != 0 && prev != 0) {
                    return "mila";
                }
                
                if(prev != 0 || prevPrev != 0) {
                    if(d == 1) return "unomila";
                    if(d == 0) return "mila";
                    
                    System.out.println("prev != 0");
                    return UNITS[d] + "mila";
                } else {
                    return d == 1 ? "mille" : "";
                }
            case 5:
                if(d == 0) return "";
                
                if(next == 0) {
                    return TENTHS[d-1];
                } else if(d == 1) {
                    return TEN_TO_NINETEEN[next];
                } else {
                    return combine(TENTHS[d - 1], UNITS[next]);
                }
            case 6:
                if(d == 0) return "";
                return UNDREDS[d - 1];
            case 7:
                if(prev == 0 && prevPrev == 0 && d == 0) return "";
                if(prev == 0) {
                    return d == 1 ? "unmilione" : UNITS[d] + "milioni";
                } else {
                    return "";
                }
            case 8://dieci milioni, undici milioni, venti milioni ecc
                if(d == 0) return "";
                
                if(next == 0) {
                    return TENTHS[d - 1] + "milioni";
                } else if(d == 1) {
                    return TEN_TO_NINETEEN[next] + "milioni";
                } else {
                    return combine(TENTHS[d - 1], UNITS[next]) + "milioni";
                }
            default: return "";
        }
    }
    
    private static final String[] UNITS =
            "zero,uno,due,tre,quattro,cinque,sei,sette,otto,nove".split(",");
    
    private static final String[] TEN_TO_NINETEEN =
            "dieci,undici,dodici,tredici,quattordici,quindici,sedici,diciassette,diciotto,diciannove".split(",");
    
    private static final String[] TENTHS =
            "dieci,venti,trenta,quaranta,cinquanta,sessanta,settanta,ottanta,novanta".split(",");
    
    private static final String[] UNDREDS =
            "cento,duecento,trecento,quattrocento,cinquecento,seicento,settecento,ottocento,novecento".split(",");
    
    private static final String[] THOUSANDS =
            "mille,duemila,tremila,quattromila,cinquemila,seimila,settemila,ottomila,novemila".split(",");
}
